\documentclass[a4paper, 12pt]{article}


\usepackage[francais]{babel}	% Langue
\usepackage[utf8]{inputenc}	% Encodage du texte
\usepackage[T1]{fontenc}		% Encodage de police
\usepackage{lmodern}			% Police

%\usepackage{textcomp}		% Symboles
%\usepackage{url}			% URLs
\usepackage{graphicx}		% Images
%\usepackage[empty]{fullpage}	% Pleine page
%\usepackage{nopageno}		% Pagination
%\usepackage{multirow}		% Tableau complexe


\title{Projet CUDA\\Programmation Mobile et Multi-Cœurs}
\author{\bsc{Paneri} Jérémy \and \bsc{Perez} Julien}
\date{Master 1 Informatique\\ 2012 -- 2013}

\begin{document}
\maketitle

\begin{figure}[!b]
   \centering\includegraphics[scale=0.2]{ufc.png}
\end{figure}

\thispagestyle{empty}
\newpage


% CONTENU %


\section{Présentation du sujet}

Une carte graphique (GPU), au contraire du processeur principal d'un ordinateur (CPU), possède de nombreuses unités de traitement pouvant s'exécuter en même temps. Elles peuvent donc être extrêmement pratiques et rapides utilisées à bon escient dans du code pouvant être facilement parallélisé.

Ce projet se base sur l'architecture CUDA inclus dans les cartes NVIDIA. Son but est, partant d'un programme «basique» en C traitant de la diffusion de particules en 3D, de transformer ce code afin qu'il soit exécutable sur GPU. Nous comparons ensuite les performances des deux méthodes.


\section{Parallélisation du code}

Notre «fonction principale GPU» se nomme \texttt{calcul\_gpu}. C'est une fonction hôte
\footnote{Une fonction hôte (ou \textit{host}) est appelée par et exécutée sur le CPU.},
laquelle appelle les trois fonctions CUDA
\footnote{Fonction \textit{device} ou \textit{kernel}, s'exécutant sur le GPU.}
correspondant au découpage des calculs nécessaires à la diffusion 3D :

\begin{itemize}
	\item \texttt{calculPosition} calcule la position des particules ;
	\item \texttt{calculVitesse} calcule leur vitesse ;
	\item \texttt{calculAcceleration} calcule leur accélération.
\end{itemize}

Ce découpage s'est fait de manière naturelle et logique. Il permet de rendre le code GPU plus clair, mais aussi plus simple à manipuler et à tester.




\subsection{Position et vitesse}

Ces deux fonctions sont assez simples et similaires : il s'agit en fait de calculs et d'affectation dans des tableaux.

La ligne de code la plus importante est celle permettant de trouver l'indice d'accès aux tableaux en fonction du block et du thread dans lequel nous nous trouvons. Il est donné par la formule \texttt{threadIdx.x + blockDim.x*blockIdx.x}.



\subsection{Accélération}

Le calcul de l'accélération nous a posé quelques problèmes. Au départ, nous avions souhaité pouvoir paralléliser les deux boucles \texttt{for} imbriquées, en gros de «dé-boucler» le code pour avoir une efficacité maximale.

Nous avons essayé une manipulation un peu différente des indices de tableaux, mais nous n'avons pas réussi. Une solution aurait été de pouvoir appeler un kernel depuis un autre kernel ; hélas ce n'est pas possible.

Nous avons donc laissé une boucle, et le principe global d'accès aux tableaux et le même que pour les fonctions précédentes.


\subsection{Blocks et threads}

Afin d'avoir une efficacité optimale, il a fallu jouer sur la manière dont on séparait les calculs : nombre de blocks, et nombre de threads par block.

Pour les trois kernels, une des répartitions les plus efficaces a été de faire appel à \texttt{nb\_p/16} blocks et \texttt{16} threads par blocks. Cependant, les différences entre les différents rapports se sont révélées minimes.





\section{Gestion dans le \texttt{main}}

Le programme principal se présente en 5 parties successives :

\begin{enumerate}
	\item Initialisation aléatoire des tableaux ;
	\item Création des variables CUDA et copie des valeurs ;
	
	\item
		\begin{enumerate}
			\item Exécution \texttt{t\_max} fois des calculs CPU,
			\item Mesure du temps d'exécution avec la fonction \texttt{clock},
			\item Affichage du temps et des valeurs calculées
			\footnote{Pour cela, nous avons deux fonctions permettant d'afficher les premières valeurs d'un tableau, contenant des \texttt{float4} ou des \texttt{float3}.} ;
		\end{enumerate}
	
	\item
		\begin{enumerate}
			\item Exécution \texttt{t\_max} fois des calculs GPU,
			\item Mesure du temps d'exécution avec des \texttt{cudaEvent},
			\item Stockage des résultats dans de nouveaux tableaux (ceci permet d'éviter les interférences avec les résultats CPU),
			\item Affichage du temps et des valeurs calculées ;
		\end{enumerate}
	
	\item Libération de la mémoire GPU et fin du programme.
\end{enumerate}










\section{Comparaison des performances}

En modifiant le nombre de particules gérées (constante \texttt{nb\_p}) entre deux exécutions, nous avons relevé les temps suivants (en secondes).

\begin{center}
\begin{tabular}{|c|c|c|}

	\hline
	\texttt{nb\_p}	&	Temps CPU	&	Temps GPU\\
	\hline
	
	64					&	0.010000	&	0.003729\\
	\hline
	
	128					&	0.040000	&	0.006466\\
	\hline
	
	256					&	0.140000	&	0.011966\\
	\hline
	
	512					&	0.570000	&	0.023016\\
	\hline
	
	1024				&	2.300000	&	0.047163\\
	\hline
	
	2048				&	9.180000	&	0.187521\\
	\hline
	
	4096				&	36.790000	&	0.560398\\
	\hline
	
\end{tabular}
\end{center}

En doublant le nombre de particules, on constate que le temps CPU est à chaque fois multiplié à peu près par quatre, alors que le temps GPU est multiplié par deux.

En d'autres termes, plus le nombre de particules est grand, plus le GPU est «rentable» par rapport au CPU : d'un rapport de 2,7 pour 64 particules, il atteint 65,7 pour 4096 particules !



\end{document}
